#!/usr/bin/env bash
set -Eeuo pipefail

####################
# Global variables
####################
# extern $APP
# extern $SUPPORT
# extern $RUN_LOCAL
CPI=$(lscpu)
SYS=$(uname -r)
HOST=$(hostname -s)
KERNEL=$(echo "$SYS" | cut -b 1)
MINOR=$(echo "$SYS" | cut -d '.' -f2)
ARCH=$(dpkg --print-architecture)
CORES=$(grep -c '^processor' /proc/cpuinfo)

info () { printf "%b%s%b" "\E[1;34m❯ \E[1;36m" "${1:-}" "\E[0m\n"; }
error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: ${1:-}" "\E[0m\n" >&2; }
warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: ${1:-}" "\E[0m\n" >&2; }

trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR

# Param[0]: RAM size
# Param[1]: Storage folder location
# Param[2]: RAM check
# Param[3]: Instance number
printMachineInfo() {

    local RAM_SZ=$1
    local STORAGE=$2
    local RAM_CHK=$3
    local NUM=$4

    if ! grep -qi "socket(s)" <<< "$CPI"; then
        SOCKETS=1
    else
        SOCKETS=$(echo "$CPI" | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
    fi

    if ! grep -qi "model name" <<< "$CPI"; then
        CPU=""
    else
        CPU=$(echo "$CPI" | grep -m 1 -i 'model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/  */ /g')
    fi

    if [ -z "${CPU// /}" ] && grep -qi "model:" <<< "$CPI"; then
        CPU=$(echo "$CPI" | grep -m 1 -i 'model:' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/  */ /g')
    fi

    CPU="${CPU// CPU/}"
    CPU="${CPU// 16 Core/}"
    CPU="${CPU// Processor/}"
    CPU="${CPU// Quad core/}"
    CPU="${CPU// Core TM/ Core}"
    CPU="${CPU// with Radeon Graphics/}"
    [ -z "${CPU// /}" ] && CPU="Unknown"

    # Read memory
    RAM_SPARE=500000000
    RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
    RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
    RAM_SZ=$(echo "${RAM_SZ^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
    RAM_WANTED=$(numfmt --from=iec "$RAM_SZ")
    RAM_WANTED=$((${RAM_WANTED}*${NUM}))
    AVAIL_GB=$(( RAM_AVAIL/1073741824 ))
    TOTAL_GB=$(( (RAM_TOTAL + 1073741823)/1073741824 ))
    WANTED_GB=$(( (RAM_WANTED + 1073741823)/1073741824 ))

    # Print system info
    SYS="${SYS/-generic/}"
    FS=$(stat -f -c %T "$STORAGE")
    FS="${FS/UNKNOWN //}"
    FS="${FS/ext2\/ext3/ext4}"
    FS=$(echo "$FS" | sed 's/[)(]//g')
    SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
    SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))

    # Check memory
    if [[ "$RAM_CHK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
        error "Your configured RAM_SIZE of $WANTED_GB GB is too high for the $AVAIL_GB GB of memory available, please set a lower value."
        exit 17
    fi

    echo "❯ CPU: ${CPU} | RAM: $AVAIL_GB/$TOTAL_GB GB | DISK: $SPACE_GB GB (${FS}) | KERNEL: ${SYS}"
    echo

    return 0
}

# MAIN function of script init.sh
# Param[0]: RAM size
# Param[1]: Storage folder location
# Param[2]: RAM check
# Param[3]: Instance number
init() {
    local RAM_SZ=$1
    local STORAGE=$2
    local RAM_CHK=$3
    local INSTANCE_NUM=$4

    if [ $RUN_LOCAL -eq 0 ]; then
    [ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
    fi

    [ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12

    echo "❯ Starting $APP for Docker v$(</run/version)..."
    echo "❯ For support visit $SUPPORT"

    if [ $RUN_LOCAL -eq 0 ]; then
    # Check & init memory system
    if [ ! -d "/dev/shm" ]; then
        error "Directory /dev/shm not found!" && exit 14
    else
        [ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm
    fi
    fi

    # Check folder
    if [ ! -d "$STORAGE" ]; then
        error "Storage folder ($STORAGE) not found!" && exit 13
    fi

    printMachineInfo $RAM_SZ $STORAGE $RAM_CHK $INSTANCE_NUM

    return 0
}


########################
# Helper functions
########################

isAlive() {
  local pid=$1

  if kill -0 "$pid" 2>/dev/null; then
    return 0
  fi

  return 1
}

pKill() {
  local pid=$1

  { kill -15 "$pid" || true; } 2>/dev/null

  while isAlive "$pid"; do
    sleep 0.2
  done

  return 0
}

fWait() {
  local name=$1

  while pgrep -f -l "$name" >/dev/null; do
    sleep 0.2
  done

  return 0
}

fKill() {
  local name=$1

  { pkill -f "$name" || true; } 2>/dev/null
  fWait "$name"

  return 0
}

addPackage() {
  local pkg=$1
  local desc=$2

  if apt-mark showinstall | grep -qx "$pkg"; then
    return 0
  fi

  MSG="Installing $desc..."
  info "$MSG" && html "$MSG"

  DEBIAN_FRONTEND=noninteractive apt-get -qq update
  DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null

  return 0
}

